#!/usr/bin/python
from genericpath import isdir
from importlib.resources import path
from logging import _srcfile
from pydoc import text
import string
import sys
import os
import json
import argparse
import re
from tokenize import String

src_temp = 'template'
dst_dir = 'test'
attributes = None
memory = None
prop = None
typedef = None

def print_err(log):
    print("\033[1;38m[err]:\033[0m\033[1;31m%s\033[0m"%log)

def print_info(log):
    print("\033[1;38m[info]:\033[0m\033[1;34m%s\033[0m"%log)   

def caplize_word(word):
    return "".join(word[:1].upper()+word[1:])

def match_word(word,Upper = False):
    match = re.match('(.*)\((.*)\)',word)
    if match is None:
        if Upper == True:
            return word.upper()
        else:
            return word
    else:
        if Upper == True:
            return match.group(1)
        else:
            return match.group(2)

## 限定来自attributes 的关键字        
def attr_text(key,st = 0):
    if st == 0:
        return match_word(attributes[key])
    elif st == 1:
        return caplize_word(attributes[key])
    elif st == 2:
        return match_word(attributes[key],True)
    else:
        return match_word(attributes[key])

def sep_text(text, sep = None):
    if sep is None:
        return "<%s>"%text
    else:
        return sep+text+sep

def keyword_text(text,key,attr, sep = None):
    word = text
    if isinstance(attr[key],str):
        word = re.sub(sep_text(key, sep), match_word(attr[key]),word)
        word = re.sub(sep_text(caplize_word(key), sep),caplize_word(attr[key]),word)
        word = re.sub(sep_text(key.upper(), sep), match_word(attr[key],True),word)
    else:
        word = re.sub(sep_text(key, sep),"%d"%attr[key],word)
    return word

def get_keyword_text(text, sep = None):
    replace = text
    list = ["subsys_name","namespace","relate_path","part_name","key_word","description",
        "adapted_system","service_id"]
    for key in list:
        replace = keyword_text(replace,key,attributes, sep)
    
    list = ["rom","ram"]
    for key in list:
        replace = keyword_text(replace,key,memory, sep)
        
    replace = re.sub("<method_count>","%d"%len(prop),replace)
    return replace

def replace_file_all_keyword(file_path):
    with open(file_path,'r+') as f:
        file = f.read()
        file = get_keyword_text(file)
        f.seek(0)
        f.write(file)
        f.truncate()
        f.close()
    
def replace_file_keyword(file_path,search,replace):
    print('============={%s}============'%file_path)
    print(replace)
    with open(file_path,'r+') as f:
        file = f.read()
        file = re.sub(search,replace,file)
        f.seek(0)
        f.write(file)
        f.truncate()
        f.close()

def copy_dir(srcDir,dstDir):
    if (not os.path.exists(srcDir)):
        print_err("%s不存在"%srcDir)
        return False
    if (not os.path.exists(dstDir)):
        os.makedirs(dstDir)
        print_info("创建%s"%dstDir)
        
    for f in os.listdir(srcDir):
        srcFile = os.path.join(srcDir,f)
        dstFile = os.path.join(dstDir,get_keyword_text(f, '%'))
        if (os.path.isfile(srcFile)):
            open(dstFile,"wb").write(open(srcFile,"rb").read())
            replace_file_all_keyword(dstFile)
            print_info("拷贝文件%s"%dstFile)
        else:
            copy_dir(srcFile,dstFile)
    return True

def copy_file(srcDir,dstDir):
    for f in os.listdir(srcDir):
        srcFile = os.path.join(srcDir,f)
        dstFile = os.path.join(dstDir,get_keyword_text(f, '%'))
        if (os.path.isfile(srcFile)):
            open(dstFile,"wb").write(open(srcFile,"rb").read())
            replace_file_all_keyword(dstFile)
            print_info("拷贝文件%s"%dstFile)

def cpp_rip(tp):
    type_ = tp
    if 'const' in type_:
        type_ = type_.replace('const',"").strip()
    if '&' in type_:
        type_ = type_.replace('&',"",1).strip()
    if '[' in type_ and ']' in type_:
        type_ += "*"
    return type_

def cpp_to_js(tp):
    tp = cpp_rip(tp)
    if is_cpp_int(tp):
        return "number"
    elif is_cpp_string(tp):
        return "string"
    else:
        return "void"

def create_template(module):
    # temp = ["utils","sa_profile","interfaces","frameworks","services","etc"]
    path = module

    if module == "jstest":
        src = os.path.join(src_temp,"frameworks/js/test")
        dst = os.path.join(dst_dir,"frameworks/js/test")
    else:
        src = os.path.join(src_temp,module)
        dst = os.path.join(dst_dir,module)

    if not copy_dir(src,dst):
        print_err("%s缺少模板"%module)
        return False
    if module == "jstest":
        return gen_jstest()
    elif module == "utils":
        return gen_utils()
    elif module == "etc":
        return gen_etc()
    elif module == "figures":
        return gen_figures()
    elif module == "interfaces":
        return gen_interfaces()
    elif module == "frameworks":
        return gen_frameworks()
    elif module == "services":
        return gen_services()
    
    return True

def gen_utils(module = "utils"):
    dst = os.path.join(dst_dir,module)
    
    common_inc = os.path.join(dst,"native/include/%s_common.h"%attributes["key_word"])
    
    defines = "#define %s %s"%(match_word(attributes["service_id"],True),match_word(attributes["service_id"]))
    replace_file_keyword(common_inc,"<defines>",defines)
    return True

def gen_etc(module = "etc"):
    return True

def gen_figures(module="figures"):
    dst = os.path.join(dst_dir,module)
    if not os.path.exists(dst):
        os.makedirs(dst)
    return True

def gen_saprofile(module="sa_profile"):
    pass

def type_cpp2js(cpp_type):
    if is_cpp_int(cpp_type):
        return "number"
    elif cpp_type == "bool":
        return "boolean"
    elif is_cpp_string(cpp_type):
        return "string"
    elif cpp_type == "void":
        return "void"
    else:
        return cpp_type
    
def build_js_params_list(key):
    param = ""
    for para in key["params"]:
        param += "    "+para+" = %s;\n"%get_default_return(get_param_type(para));
    return param

def build_params_list(key,offset_char='    ',alloc=True):
    return build_params_list2(key["params"],offset_char,alloc)

def build_params_list2(params,offset_char='    ',alloc = True):
    param = ''
    for para in params:
        type_ = get_param_type(para)
        name_ = get_param_name(para)
        if '&' in type_:
            type_ = type_.replace('&',"",1).strip()
        if alloc:
            if 'const' in type_:
                type_ = type_.replace('const',"").strip()
        
            if '*' in type_:
                type_ = type_.replace('*',"",1)
                param += f'{offset_char}{type_} {name_}[128] = {{0}};\n'
            else:
                param += f'{offset_char}{type_} {name_} = {get_default_return(type_)};\n'
        else:
            param += f'{offset_char}{type_} {name_} = {get_default_return(type_)};\n'
    return param

def build_params_list3(params,offset_char='    '):
    param = ''
    for para in params:
        type_ = get_param_type(para)
        name_ = get_param_name(para)
        if '&' in type_:
            type_ = type_.replace('&',"",1).strip()
        
        if 'const' in type_:
            type_ = type_.replace('const',"").strip()
        
        if '*' in type_:
            type_ = type_.replace('*',"",1)
            param += f'{offset_char}{type_} {name_}[128];\n'
        else:
            param += f'{offset_char}{type_} {name_};\n'
    return param

def get_param_type(para):
    list0 = para.split(':')[0]
    list1 = list0.split()
    type_ = ""
    for k in list1[0:-1]:
        if type_ != "":
            type_ += " "
        type_ += k
    if "*" in list1[-1]:
        type_ += "*"
    if "&" in list1[-1]:
        type_ += ""
    if "[" in list1[-1] and "]" in list0[-1]:
        type_ += "*"
    if '&' in type_:
        type_ = type_.replace('&',"",1).strip()
        
    if 'const' in type_:
        type_ = type_.replace('const',"").strip()
    return type_

def get_param_flag(para):
    if ":" in para:
        flag = para.split(':')[-1]
        return flag
    return 'in'

def get_param_name(para):
    list0 = para.split(':')[0].split()
    para = list0.pop()
    para = para.replace("*","")
    para = para.replace("&","")
    para = para.replace("[]","")
    return para

def get_param_value(para):
    list0 = para.split(':')
    return list0.pop()

def get_param_list(key):
    param = ""
    for para in key["params"]:
        if param != "":
            param +=','
        para = para.split(':')[0]
        param += para
    return param

def is_cpp_int(tp,bits = None):
    types = {}
    types['8'] = ["char","int8_t","uint8_t"]
    types['16'] = ["short","int16_t","uint16_t"]
    types['32'] = ["int","int32_t","uint32_t"]
    types['64'] = ["long long","int64_t","uint64_t"]
    
    if bits is None:
        if tp in types['8'] or tp in types['16'] or tp in types['32'] or tp in types['64']:
            return True
    else:
        if tp in types[bits]:
            return True
    return False

def is_cpp_float(tp):
    float_type = ["float","double"]
    return tp in float_type

def is_cpp_string(tp):
    if "string" in tp:
        return True
    elif "char" in tp and "*" in tp:
        return True
    return False

def get_default_return(type_,ret = -1):
    if "char" in type_ and "*" in type_:
        return "NULL"
    elif "char" in type_ and "*" not in type_:
        return ret
    elif "*" in type_:
        return "NULL"
    elif is_cpp_int(type_):
        return ret
    elif type_ == "bool":
        return "false"
    elif "string" in type_:
        return ""
    elif type_== "void":
        return ""
    else:
        return "{}"
    
def get_default_define(type_,name):
    if type_ == "void":
        return ""
    code = "%s %s = %s"%(type_,name,get_default_return(type_))
    return code

def get_return_by_type(type_,name):
    if type_ == "void":
        return ""
    return name
    
def build_func_call(key):
    func_call = key["method"]
    params = ""
    for param in key["params"]:
        if params != "":
            params +=','
        params += get_param_name(param)
    func_call +="("+params+")";
    return func_call

def build_cpp_declare(cpp,abstract = True,override = False):
    declare = "    /**\n     * %s\n     */\n"%cpp["note"]
    
    if abstract:
        declare += "    virtual %s %s(%s) = 0;\n"%(cpp["type"],cpp["method"],get_param_list(cpp))
    else:
        if override is True:
            declare += "    %s %s(%s) override;\n"%(cpp["type"],cpp["method"],get_param_list(cpp))
        else:
            declare += "    %s %s(%s);\n"%(cpp["type"],cpp["method"],get_param_list(cpp))
    return declare;

def build_cpp_define(cpp,clss,code = '/* Add Code */'):
    param = ""
    for para in cpp["params"]:
        if param != "":
            param +=','
        para = para.split(':')[0]
        param += para
    define = "%s %s::%s(%s)\n{\n    %s\n}\n"%(cpp["type"],clss,caplize_word(cpp["method"]),param,code)
    return define

def get_js_param_list(key):
    param = ""
    for para in key["params"]:
        if param != "":
            param +=','
        type_ = get_param_type(para)
        name_ = get_param_name(para)
        param += f"{name_}:{type_cpp2js(type_)}"
    return param

def build_js_declare(key):
    declare = "    /**\n     * %s\n     * @since 6\n     */\n"%key["note"]
    declare += f"    function {key['method']}({get_js_param_list(key)}): {type_cpp2js(key['type'])};\n"
    return declare

def gen_interfaces(module="interfaces"):
    dst = os.path.join(dst_dir,module)
    
    ts_dst = os.path.join(dst,"kits/js/@ohos.%s.d.ts"%attributes["key_word"])
    js_declare = "" #"declare namespace %s {\n"%attributes["key_word"]
    
    for key in typedef:
        defs = ""
        for val in key["values"]:
            defs += f"        {val['def']}"
            if val['value'] != "":
                defs += f" = {val['value']}"
            defs += ","
            if val['note'] != "":
                defs += f" //{val['note']}"
            defs +="\n"
        def_declare = f"\n    export {key['type']}  {key['def']}{{\n{defs}    }}\n"
        js_declare += def_declare

    for key in prop:
        js_declare += build_js_declare(key)

    #js_declare += "}\n"
    replace_file_keyword(ts_dst,"<declare>",js_declare)

    def_dst = os.path.join(dst,"innerkits/native/include/%s_def.h"%attributes["key_word"])
    srv_dst = os.path.join(dst,"innerkits/native/include/i%s_srv.h"%attributes["key_word"])
    srvclient_dst = os.path.join(dst,"innerkits/native/include/%s_srv_client.h"%attributes["key_word"])

    srv_declare = "\n"
    srv_enum = "\n"
    for key in prop:
        srv_declare += build_cpp_declare(key)
        srv_enum += "        %s_%s,\n"%(attr_text("key_word",2),key["method"].upper())
    replace_file_keyword(srv_dst,"<declare>",srv_declare)
    replace_file_keyword(srv_dst,"<enum>",srv_enum)

    srv_declare = "\n"
    for key in prop:
        srv_declare += build_cpp_declare(key,False)
    replace_file_keyword(srvclient_dst,"<declare>",srv_declare)

    srv_declare = "\n"    
    for key in typedef:
        defs = ""
        for val in key["values"]:
            defs += f"    {val['def']}"
            if val['value'] != "":
                defs += f" = {val['value']}"
            defs += ","
            if val['note'] != "":
                defs += f" //{val['note']}"
            defs +="\n"
        srv_declare += f"typedef {key['type']} __{key['def']} {{\n{defs}}}{key['def']};\n"
    replace_file_keyword(def_dst,"<declare>",srv_declare)
    return True

def type_cpptonapi(type_):
    if 'const' in type_:
        type_ = type_.replace('const',"").strip()
    if '&' in type_:
        type_ = type_.replace('&',"",1).strip()
    if type_ == "char" or type_ == "short" or type_ == "int" or type_ == "long" or type_ == "int8_t" or type_=="int16_t" or type_ == "int32_t":
        return "int32"
    elif type_ == "unsigned char" or type_ == "unsigned short" or type_ == "unsigned int" or type_ == "unsigned long" or type_ == "uint8_t" or type_=="uint16_t" or type_ == "uint32_t":
        return "uint32"
    elif type_ == "int64_t":
        return "int64"
    elif type_ == "uint64_t" or type_ == "unsigned long long":
        return "uint64"
    elif type_ == "bool":
        return "bool"
    elif ("char" in type_ and "*" in type_) or ("char" in type_ and "[" in type_ and "]" in type_):
        return "string_utf8"
    elif type_ == "float" or type_ == "double":
        return "double"
    elif "*" in type_:
        return "array"
    else :
        return None

def build_napi2cpp_params(ret_type,params,offset_char='    '):
    napi_in_type = f'{offset_char}struct tTmpStruct{{\n'
    napi_in_value = f'{offset_char}tTmpStruct *pVar = new tTmpStruct();\n'
    napi_in_conv = ''
    param = ''
    index = 0

    for para in params:
        cpp_type = get_param_type(para)
        type_ = type_cpptonapi(cpp_type)
        name_ = get_param_name(para)
        ori_type = cpp_type

        if '*' in cpp_type:
            cpp_type = cpp_type.replace('*',"",1)
            napi_in_type += f'    {offset_char}{cpp_type} {name_}[256];\n'
            napi_in_conv += f'    {offset_char}{ori_type} {name_} = pVar->{name_};\n'
        else:
            napi_in_type += f'    {offset_char}{cpp_type} {name_};\n'
            napi_in_conv += f'    {offset_char}{ori_type} &{name_} = pVar->{name_};\n'

        if type_ is None:
            param += f'{offset_char}(void)pVar->{name_};/* fix me: 需要手动添加转换 */\n'
        elif type_ == "string_utf8":
            param += f'{offset_char}size_t {name_}Len = 0;\n'
            param += f'{offset_char}NAPI_CALL(env,napi_get_value_{type_}(env,args[{index}],pVar->{name_},NAPI_AUTO_LENGTH,&{name_}Len));\n'
        else:
            param += f'{offset_char}NAPI_CALL(env,napi_get_value_{type_}(env,args[{index}],&pVar->{name_}));\n'
        index = index+1

    if ret_type != "void":
        napi_in_type += f'    {offset_char}{ret_type} ret;\n'
        napi_in_conv += f'    {offset_char}{ret_type} &ret = pVar->ret;\n'
    napi_in_type +="    };\n"
    return param,napi_in_type,napi_in_value,napi_in_conv

def build_napi_call(ntype,val,result):
    napi_call = ""
    if ntype is None:
        napi_call = f'napi_create_object(env,{val},&{result})'
    elif ntype == "string_utf8":
        napi_call = f'napi_create_{ntype}(env,{val},NAPI_AUTO_LENGTH,&{result})'
    elif ntype == "bool":
        napi_call = f'napi_get_boolean(env,{val},&{result})'
    elif ntype == "void":
        napi_call += f'        NAPI_CALL(env,napi_get_undefined(env,&{result}));\n'
    else:
        napi_call = f'napi_create_{ntype}(env,{val},&{result})'
    return napi_call

def get_classic_params(params):
    out_params = []
    in_params = []
    for para in params:
        if get_param_flag(para) == 'out':
            out_params.append(para)
        else:
            in_params.append(para)
    return in_params,out_params

def build_napi_function(key,napi_type,in_params,out_params):
    napi_out_cpp = "        tTmpStruct *pVar = (tTmpStruct *)data;\n        napi_value result;\n"

    if len(out_params) == 0:
        napi_out_cpp += f'        NAPI_CALL(env,{build_napi_call(napi_type,"pVar->ret","result")});\n'
    elif len(out_params) == 1:
        napi_out_cpp += f"        (void)pVar->ret;\n"
        for out in out_params:
            ntype = type_cpptonapi(get_param_type(out))
            nname = get_param_name(out)
            napi_out_cpp += f'        NAPI_CALL(env,{build_napi_call(ntype,nname,"result")});\n'; 
    else:
        napi_out_cpp += f'        napi_value obj;\n'
        napi_out_cpp += f"        (void)pVar->ret;\n"
        napi_out_cpp += f'        NAPI_CALL(env,napi_create_object(env,&obj));\n'
        
        for out in out_params:
            ntype = type_cpptonapi(get_param_type(out))
            nname = get_param_name(out)
            if ntype is None:
                napi_out_cpp +=""
            elif ntype == "string_utf8":
                napi_out_cpp +=f'        NAPI_OBJECT_PROP_SET_WITH_LEN(obj,{ntype},\"{nname}\",strlen({nname}),{nname});\n'
            else:
                napi_out_cpp +=f'        NAPI_OBJECT_PROP_SET(obj,{ntype},\"{nname}\",{nname});\n'
        napi_out_cpp += f'        result = obj;\n'

    napi_params,napi_in_type,napi_in_value,napi_in_conv = build_napi2cpp_params(key["type"],in_params+out_params,"    ")
    
    napi_to_cpp = f'    napi_value this_arg = nullptr;\n'\
        f'    void *priv_data = nullptr;\n'\
        f'    size_t argc = {len(in_params)};\n'\
        f'    napi_value args[{len(in_params)}] = {{0}};\n'\
        f'{napi_in_type}\n'\
        f'{napi_in_value}\n'\
        f'    NAPI_CALL(env,napi_get_cb_info(env,info,&argc,args,&this_arg,&priv_data));\n'\
        f'    NAPI_ASSERT(env, argc >= {len(in_params)}, "Wrong number of arguments");\n'

    
    # cpp_params = build_params_list2(out_params,"        ")
    #  f'{cpp_params}\n'\
    work_func = f'{napi_to_cpp}'\
        f'{napi_params}\n'\
        f'    async_excute_work work_func = [](napi_env env,size_t argc,napi_value args[],void *data)->napi_status {{\n'\
        f'        tTmpStruct *pVar = (tTmpStruct *)data;\n'\
        f'{napi_in_conv}\n';

    if key["type"] == "void":
        work_func += f'        g_{attributes["key_word"]}Client.{build_func_call(key)};\n'
    else:
        work_func += f'        ret = g_{attributes["key_word"]}Client.{build_func_call(key)};\n'

    work_func += f'    \n'\
        f'        return napi_ok;\n'\
        f'    }};\n'\
        f'    async_result_work result_func = [] (napi_env env,void *data)->napi_value {{\n'\
        f'{napi_out_cpp}\n'\
        f'        return result;\n'\
        f'    }};\n'\
        f'    async_free_work free_func = [] (napi_env env,void *data) {{\n'\
        f'        tTmpStruct *pVar = (tTmpStruct *)data;\n'\
        f'        delete pVar;\n'\
        f'    }};\n'\
        f'    AsyncCallback *async = new AsyncCallback(env,argc,args,work_func,result_func,free_func,\n'\
        f'        pVar,\n'\
        f'        this_arg,\n'\
        f'        priv_data);\n'\
        f'#if 1 // 根据需要选择\n'\
        f'    return NapiCall(env,async);\n'\
        f'#else\n'\
        f'    return AsyncPromise(env,\"{key["method"]}\",async);\n'\
        f'#endif\n'
    
    declare = f'static napi_value {key["method"]}(napi_env env, napi_callback_info info)\n'\
        f'{{\n'\
        f'{work_func}'\
        f'}}\n'
    return declare

def build_napi_api(key):
    napi_type = type_cpptonapi(key["type"])
    in_params,out_params = get_classic_params(key["params"])
    print(f'in:{in_params},out:{out_params}')
    return build_napi_function(key,napi_type,in_params,out_params)
    

def build_js_enum(key):
    size = len(key['values'])

    if size == 0:
        return
    code = f"    napi_value values[{size}] = {{0}};\n"
    index = 0
    code_def = ""
    code_declare = "    napi_property_descriptor desc[] = {\n"
    for val in key['values']:
        code_def += f"    napi_create_int32(env, (int32_t){val['def']}, &values[{index}]);\n"
        code_declare += f"        DECLARE_NAPI_STATIC_PROPERTY(\"{val['def']}\", values[{index}]),\n"
        index = index+1

    code_declare += "    };\n"
    code += code_def
    code += code_declare

    code += f"    napi_value result = nullptr;\n"
    
    code += f"    napi_define_class(env, \"{key['def']}\", NAPI_AUTO_LENGTH, EnumClassConstructor, nullptr,{size}, desc, &result);\n"
    code += f"    napi_set_named_property(env, exports, \"{key['def']}\", result);\n"
    
    define = f"static napi_value CreateEnum{caplize_word(key['def'])}(napi_env env,napi_value exports)\n{{\n{code}    return exports;\n}}\n"
    return define

def build_client_test(key,index):
    test_method = f"{attr_text('key_word',1)}Client{'%03d' % (index) }"
    param_list = build_params_list(key,'        ')
    func_call = build_func_call(key)
    code = f"/**\n"\
        f" * @tc.name: { test_method }\n"\
        f" * @tc.desc: Test {attr_text('key_word',1)} interface {key['method']}\n"\
        f" * @tc.type: FUNC\n"\
        f" */\n"\
        f"HWTEST_F ({attr_text('key_word',1)}ClientTest,{test_method},TestSize.Level1)\n"\
        f"{{\n"\
        f"    {attr_text('key_word',2)}_HILOGD({attr_text('key_word',2)}_TEST,\"{attr_text('key_word',1)}Client::{test_method} start.\");\n"\
        f"    auto& client = {attr_text('key_word',1)}SrvClient::GetInstance();\n"\
        f"    if (g_isMock) {{\n"\
        f"{param_list};\n"\
        f"        auto ret = client.{func_call};\n"\
        f"        {attr_text('key_word',2)}_HILOGI({attr_text('key_word',2)}_TEST,\"{attr_text('key_word',1)}ClientTest::ret=%{{public}}d\",ret);\n"\
        f"        GTEST_LOG_(INFO) << \"{attr_text('key_word',1)}Client::{test_method} executing, ret = \" << ret;\n"\
        f"        ASSERT_TRUE(ret == 0);\n"\
        f"    }} else {{\n"\
        f"{param_list};\n"\
        f"        auto ret = client.{func_call};\n"\
        f"        {attr_text('key_word',2)}_HILOGI({attr_text('key_word',2)}_TEST,\"{attr_text('key_word',1)}ClientTest::ret=%{{public}}d\",ret);\n"\
        f"        GTEST_LOG_(INFO) << \"{attr_text('key_word',1)}Client::{test_method} executing, ret = \" << ret;\n"\
        f"        ASSERT_TRUE(ret == 0);\n"\
        f"    }}\n"\
        f"    {attr_text('key_word',2)}_HILOGD({attr_text('key_word',2)}_TEST,\"{attr_text('key_word',1)}Client::{test_method} end.\");\n"\
        f"}}"
             
    return code

def build_js_unit(key):
    napi_type = type_cpptonapi(key["type"])
    in_params,out_params = get_classic_params(key["params"])

    in_vars = ""
    vars_list = ""
    for _in in in_params:
        ntype = get_param_type(_in)
        nname = get_param_name(_in)
        in_vars += f"    var {nname} = {get_default_return(ntype)}\n"
        if vars_list != "":
            vars_list += ","
        vars_list += nname

    test_method = f"{in_vars}"\
        f"    {attr_text('key_word',0)}.{key['method']}({vars_list}).then((result) => {{\n"\
        f"        console.log(\"{key['method']}:\"+result)\n"\
        f"        expect(result).assertTrue()\n"\
        f"        done()\n"\
        f"    }}).catch((err) => {{\n"\
        f"        console.log(\"{key['method']}:\"+err)\n"\
        f"        expect(err).assertTrue()\n"\
        f"    }})"
    return test_method

def gen_frameworks(module="frameworks"):
    dst = os.path.join(dst_dir,module)
    
    cpp_dst = os.path.join(dst,"native/src/%s_srv_client.cpp"%attributes["key_word"])
    cpp_define = "\n"
    clss = keyword_text("<Key_word>SrvClient","key_word",attributes)
    code = ''
    for key in prop:
        code = "int ret = Connect();\n"\
            "    if (ret != ERR_OK) {\n"\
            "        %s_HILOGE(%s_JS_NAPI, \"%s failed\");\n"\
            "        return %s;\n    }\n"\
            "    return proxy_->%s;"%(attributes["key_word"].upper(),attributes["key_word"].upper(),key["method"],get_default_return(key["type"]),build_func_call(key))
        cpp_define += build_cpp_define(key,clss,code)+"\n"
    replace_file_keyword(cpp_dst,"<declare>",cpp_define)

    js_dst = os.path.join(dst,"js/napi/%s_napi.cpp"%attributes["key_word"])
    js_define = ""
    js_api = ""
    js_enum = ""
    for key in prop:
        js_define += build_napi_api(key)
        if js_api != "":
            js_api += "    "
        js_api += "DECLARE_NAPI_FUNCTION(\"%s\", %s),\n"%(caplize_word(key["method"]),caplize_word(key["method"]))
    replace_file_keyword(js_dst,"<define_api>",js_define)
    replace_file_keyword(js_dst,"<declare_getter>",js_api)

    js_define = ""
    for key in typedef:
        if key["type"] == "enum":
            js_define += build_js_enum(key)
            if js_enum != "":
                js_enum += "    "
            js_enum += "CreateEnum%s(env, exports);\n"%caplize_word(key["def"])
    replace_file_keyword(js_dst,"<define_enum>",js_define)
    replace_file_keyword(js_dst,"<create_enum>",js_enum)

    # js unit test
    # unit test
    test_cpp = os.path.join(dst,"native/test/unittest/src/%s_client_test.cpp"%attributes["key_word"])
    
    test_define = ""
    index = 0
    for key in prop:
        index = index+1
        test_define += build_client_test(key,index)+"\n"
    replace_file_keyword(test_cpp,"<defines>",test_define)
    return True

def gen_jstest(module="jstest"):
    dst = os.path.join(dst_dir,"frameworks/js/test")

    js_unit_dst = os.path.join(dst,'%s_unit.test.js'%attributes['key_word'])
    js_unit_define = ""
    for key in prop:
        js_unit= build_js_unit(key)
        js_unit_define += f"it(\'{caplize_word(key['method'])}\',0,function() {{\n"\
                f"{js_unit}\n"\
                f"}})\n"
    replace_file_keyword(js_unit_dst,"<define>",js_unit_define)
    return True

def get_pacel_type(type_):
    if type_ == "char" or type_ == "int8_t":
        return "Int8"
    elif type_ == "unsigned char" or type_ == "uint8_t":
        return "Uint8"
    elif type_ == "short" or type_ == "int16_t":
        return "Int16"
    elif type_ == "unsigned short" or type_ == "uint16_t":
        return "Uint16"
    elif type_ == "int" or type_ == "int32_t":
        return "Int32"
    elif type_ == "unsigned int" or type_ == "uint32_t":
        return "Uint32"
    elif type_ == "long long" or type_ == "int64_t":
        return "Int64"
    elif type_ == "unsigned long long" or type_ == "uint64_t":
        return "Uint64"
    elif type_ == "float":
        return "Float"
    elif type_ == "double":
        return "Double"
    elif type_ == "bool":
        return "Bool"
    elif "string" in type_ or ("char" in type_ and ("*" in type_ or ("[" in type_ and "]" in type_))):
        return "String"
    elif type_ == "void":
        return ""
    else:
        return "Unknown"
    
def write_pacel_param(cpp_type,name,var,ret):
    pacel_type = get_pacel_type(cpp_type)
    if pacel_type == "Unknown":
        return f"    (void){name};// fix me: 需要手动转换\n"
    elif pacel_type == "":
        return ''
    elif pacel_type == "String":
        return f"    WRITE_PARCEL_WITH_RET({var},{pacel_type},{name},{ret});\n"
    else:
        return f"    WRITE_PARCEL_WITH_RET({var},{pacel_type},{name},{ret});\n"

def read_pacel_param(cpp_type,name,var,ret):
    pacel_type = get_pacel_type(cpp_type)
    
    if pacel_type == "Unknown":
        return f"    READ_BUFFER_WITH_RET({var},{cpp_type},{name},sizeof({name}),{ret});\n"
    elif pacel_type == "String":
        str = f"    std::string str{name};\n"
        str += f"    READ_PARCEL_WITH_RET({var},{pacel_type},str{name},{ret});\n"
        str += f"    {name} = str{name}.c_str();\n"
        return str;
    else:
        return f"    READ_PARCEL_WITH_RET({var},{pacel_type},{name},{ret});\n"
    
def write_pacel_param_list(key,var,ret,flag = None):
    pacel_code = ""
    for para in key["params"]:
        if flag != None:
            if get_param_flag(para) != flag:
                continue
        cpp_type = get_param_type(para)
        name = get_param_name(para)
        pacel_code += write_pacel_param(cpp_type,name,var,ret)
    return pacel_code

def read_pacel_param_list(key,var,ret,flag = None):
    pacel_code = ""
    for para in key["params"]:
        if flag != None:
            if get_param_flag(para) != flag:
                continue
        cpp_type = get_param_type(para)
        name = get_param_name(para)
        pacel_code += read_pacel_param(cpp_type,name,var,ret)
    return pacel_code
    
def build_proxy_code(key):
    result = ""
    if key["type"] != "void":
        result = " result"
    else:
        result = ""
    ret = get_default_return(key["type"])
    pacel_read = read_pacel_param_list(key,'reply',ret,'out')
    pacel_read += read_pacel_param(key['type'],'result','reply',ret)
    pacel_write = write_pacel_param_list(key,'data',ret,'in')
       
    code = f'{get_default_define(key["type"],"result")};\n'\
        f'    sptr<IRemoteObject> remote = Remote();\n'\
        f'    if(remote == nullptr) return{result};\n'\
        f'    MessageParcel data;\n'\
        f'    MessageParcel reply;\n'\
        f'    MessageOption option;\n'\
        f'    \n'\
        f'    if (!data.WriteInterfaceToken({caplize_word(attributes["key_word"])}SrvProxy::GetDescriptor())) {{\n'\
        f'        {attributes["key_word"].upper()}_HILOGE({attributes["key_word"].upper()}_SRV, \"Write descriptor failed\");\n'\
        f'        return{result};\n'\
        f'    }}\n'\
        f'    \n'\
        f'{pacel_write}'\
        f'    \n'\
        f'    int ret = remote->SendRequest(static_cast<int>(I{attr_text("key_word",1)}Srv::{attr_text("key_word",2)}_{key["method"].upper()}), data, reply, option);\n'\
        f'\n    if (ret != ERR_OK) {{\n'\
        f'        {attr_text("key_word",2)}_HILOGE({attr_text("key_word",2)}_SRV, \"SendRequest is failed, ret: %d\", ret);\n'\
        f'        return{result};\n'\
        f'    }}\n'\
        f'{pacel_read}\n'\
        f'    return{result};'
    return code

def build_stub_code(key):
    result = ""
    result_call = ""
    if key["type"] != "void":
        result_call = key["type"]+" result = "+build_func_call(key)+";"
        result = " result"
    else:
        result_call = build_func_call(key)+";"
        result = ""
       
    param_list = build_params_list(key,'    ',False)
    pacel_read = read_pacel_param_list(key,'data','E_READ_PARCEL_ERROR','in')
    pacel_write = write_pacel_param_list(key,'reply','E_WRITE_PARCEL_ERROR','out')
    pacel_write += write_pacel_param(key['type'],"result",'reply','E_WRITE_PARCEL_ERROR')
    code =""
    if param_list.strip('    ').strip('\n').strip() != '':
        code += f"{param_list}\n"
    if pacel_read.strip('    ').strip('\n').strip()  != '':
        code += f"{pacel_read}\n"
    if result_call.strip('    ').strip('\n').strip() != '':
        code +=f"    {result_call}\n"
    if pacel_write.strip('    ').strip('\n').strip() != '':
        code += f"{pacel_write}\n"
    code += f"    return ERR_OK;"
    return code

def gen_services(module="services"):
    dst = os.path.join(dst_dir,module)
    
    stub_h = os.path.join(dst,"zidl/include/%s_srv_stub.h"%attributes["key_word"])
    stub_cpp = os.path.join(dst,"zidl/src/%s_srv_stub.cpp"%attributes["key_word"])
    clss = "%sSrvStub"%caplize_word(attributes["key_word"])
    cpp_case = "\n"
    cpp_declare = "\n"
    cpp_define = "\n"
    for key in prop:
        cpp_case += "        case static_cast<int>(I%sSrv::%s_%s): {\n            return %sStub(data,reply,option);\n        }\n"%(caplize_word(attributes["key_word"]),
            attributes["key_word"].upper(),key["method"].upper(),key["method"])
        cpp_declare += "    int32_t %sStub(MessageParcel& data,MessageParcel& reply,MessageOption& option);\n"%key["method"]
        cpp_define += "int32_t %s::%sStub(MessageParcel& data,MessageParcel& reply,MessageOption& option)\n{\n"\
            "%s\n"\
            "}\n"%(clss,key["method"],build_stub_code(key))
    replace_file_keyword(stub_cpp,"<case_cpp>",cpp_case)
    replace_file_keyword(stub_h,"<declare>",cpp_declare)
    replace_file_keyword(stub_cpp,"<define>",cpp_define)

    proxy_h = os.path.join(dst,"zidl/include/%s_srv_proxy.h"%attributes["key_word"])
    proxy_cpp = os.path.join(dst,"zidl/src/%s_srv_proxy.cpp"%attributes["key_word"])
    clss = "%sSrvProxy"%caplize_word(attributes["key_word"])
    cpp_declare = "\n"
    cpp_define = "\n"
    for key in prop:
        cpp_declare += build_cpp_declare(key,False,True)
        cpp_define += build_cpp_define(key,clss,build_proxy_code(key))+"\n"

    replace_file_keyword(proxy_h,"<declare>",cpp_declare)
    replace_file_keyword(proxy_cpp,"<define>",cpp_define)

    srv_h = os.path.join(dst,"native/include/%s_service.h"%attributes["key_word"])
    srv_cpp = os.path.join(dst,"native/src/%s_service.cpp"%attributes["key_word"])
    clss = "%sService"%caplize_word(attributes["key_word"])
    cpp_declare = "\n"
    cpp_define = "\n"
    for key in prop:
        cpp_declare += build_cpp_declare(key,False,True)
        result_call = ""
        result = ""
        if key["type"] != "void":
            result_call = "%s;\n    (void)result;"%get_default_define(key["type"],"result")
            result = " result"
        code = "%s;\n    \n    return%s;"%(result_call,result)
        cpp_define += build_cpp_define(key,clss,code)+"\n"
        
    replace_file_keyword(srv_h,"<declare>",cpp_declare)
    replace_file_keyword(srv_cpp,"<define>",cpp_define)
    return True

def parse_args():
    parser = argparse.ArgumentParser(description="创建模板")
    parser.add_argument('--src_temp',default='template',help='源模板路径')
    parser.add_argument('--dst_dir',default=dst_dir,help="创建路径")
    parser.add_argument('--gen_target',default='all',help="生成目标 ['all','frameworks','etc','sa_profile','interfaces','services','utils','figures','jstest']")
    parser.add_argument('--config',default='config.json',help="配置文件")
    args = parser.parse_args()
    return args

if(__name__=="__main__"):
    args = parse_args()

    print(args)
    src_temp = args.src_temp
    dst_dir = args.dst_dir
    config = args.config
    temp = args.gen_target
    conf = None
    with open(config,'r') as f:
        conf = json.load(f);
    attributes = conf["attributes"]
    subsys_name = attributes["subsys_name"]
    description = attributes["description"]
    adapted_system = attributes["adapted_system"]
    part_name = attributes["part_name"]
    service_name = f'{part_name}_service'
    key_word = attributes["key_word"]
    
    memory = conf["memory"]
    rom = memory["rom"]
    ram = memory["ram"]
    typedef = conf["typedef"]
    prop = conf["property"]
    
    modules = []
    if temp == 'all':
        modules = ["utils","etc","sa_profile","interfaces","frameworks","services","jstest"]
    else:
        modules = [temp]
    for module in modules:
        if not create_template(module):
            print_err(f'{module} 生成失败!')
            exit()
    
    if temp == 'all':
        copy_file(src_temp,dst_dir)
        
    print_info(f"子系统服务名称:{subsys_name}")
    print_info(f"服务部件名称:{part_name}")
    print_info(f"关键字:{key_word}")
    print_info(f"服务部件描述:{description}")
    print_info(f"适配系统:{adapted_system}")

    print_info(f"rom:{rom};ram:{ram}")
    print("位于%s"%(os.path.join(os.path.curdir,dst_dir)))
    print("----------Done----------")
